package com.xmindguoguo.boot.config.web;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.xmindguoguo.boot.config.properties.GunsProperties;
import com.xmindguoguo.boot.core.shiro.DBSessionDao;
import com.xmindguoguo.boot.core.shiro.ShiroDbRealm;
import com.xmindguoguo.boot.modular.monitor.service.ITSystemSessionService;
import com.xmindguoguo.boot.modular.system.service.ITSystemMenuService;

/**
 * shiro权限管理的配置
 *
 * @author fengshuonan
 * @date 2016年11月14日 下午3:03:44
 *       EnvironmentAware:凡注册到Spring容器内的bean，实现了EnvironmentAware接口重写setEnvironment方法后，在工程启动时可以获得application.properties的配置文件配置的属性值。
 */
@Configuration
//@ComponentScan({ "com.xmindguoguo.boot.modular.system.service" })
public class ShiroConfig {
    @Autowired
    ITSystemSessionService systemSessionService;
    @Autowired
    ITSystemMenuService systemMenuService;

    /**
     * 安全管理器
     */
    @Bean
    public DefaultWebSecurityManager securityManager(CookieRememberMeManager rememberMeManager, CacheManager cacheShiroManager,
            SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(this.shiroDbRealm());
        securityManager.setCacheManager(cacheShiroManager);
        securityManager.setRememberMeManager(rememberMeManager);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    // 自定义sessionDao，持久化到数据库
    @Bean
    public DBSessionDao sessionDao() {
        DBSessionDao sessionDao = new DBSessionDao();
        sessionDao.setSystemSessionService(systemSessionService);
        return sessionDao;
    }

//    /**
//     * spring session管理器（多机环境）
//     */
//    @Bean
//    @ConditionalOnProperty(prefix = "guns", name = "spring-session-open", havingValue = "true")
//    public ServletContainerSessionManager servletContainerSessionManager() {
//        return new ServletContainerSessionManager();
//    }

    /**
     * session管理器(单机环境)
     */
    @Bean
//    @ConditionalOnProperty(prefix = "guns", name = "spring-session-open", havingValue = "false")
    public DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager, GunsProperties gunsProperties) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 重写session Dao 保存到数据库
//        sessionManager.setSessionDAO(sessionDao());
        sessionManager.setCacheManager(cacheShiroManager);
        // session 验证失效时间（默认为15分钟 单位：秒）
        sessionManager.setSessionValidationInterval(gunsProperties.getSessionValidationInterval() * 1000);
        // session 失效时间（默认为30分钟 单位：秒）
        sessionManager.setGlobalSessionTimeout(gunsProperties.getSessionInvalidateTime() * 1000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
        cookie.setName("shiroCookie");
        cookie.setHttpOnly(true);
        sessionManager.setSessionIdCookie(cookie);
        return sessionManager;
    }

    /**
     * 缓存管理器 使用Ehcache实现
     */
    @Bean
    public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(ehcache.getObject());
        return ehCacheManager;
    }

    /**
     * 项目自定义的Realm
     */
    @Bean
    public ShiroDbRealm shiroDbRealm() {
        return new ShiroDbRealm();
    }

    /**
     * rememberMe管理器, cipherKey生成见{@code Base64Test.java}
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {
        CookieRememberMeManager manager = new CookieRememberMeManager();
        manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));
        manager.setCookie(rememberMeCookie);
        return manager;
    }

    /**
     * 记住密码Cookie
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        //// 设置Cookie在客户端浏览器中保存内容的cookie的名字
        SimpleCookie simpleCookie = new SimpleCookie(FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM);
        //// 证该系统不会受到跨域的脚本操作攻击
        simpleCookie.setHttpOnly(true);
        // 定义该Cookie的过期时间,单位为秒。如果设置为-1标识浏览器关闭就失效
        simpleCookie.setMaxAge(7 * 24 * 60 * 60);// 7天
        return simpleCookie;
    }

    /**
     * Shiro的过滤器链
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        /**
         * 默认的登陆访问url
         */
        shiroFilter.setLoginUrl("/login");
        /**
         * 登陆成功后跳转的url
         */
        shiroFilter.setSuccessUrl("/");
        /**
         * 没有权限跳转的url
         */
        shiroFilter.setUnauthorizedUrl("/common/unauthorized");
        /**
         * 配置shiro拦截器链
         *
         * anon 不需要认证 authc 需要认证 user 验证通过或RememberMe登录的都可以
         *
         * 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的
         *
         * 顺序从上到下,优先级依次降低
         *
         */
        Map<String, String> hashMap = new LinkedHashMap<String, String>();
        hashMap.put("/static/**", "anon");
        hashMap.put("/login", "anon");
        hashMap.put(" /common/error/**", "anon");
        hashMap.put("/register", "anon");
        hashMap.put("/share/**", "anon");
        hashMap.put("/global/sessionError", "anon");
        hashMap.put("/images/captcha", "anon");
        // 增加数据库中不需要查询的URL
//        Map<String, String> noLoginAndPer = new LinkedHashMap<String, String>();
//        Wrapper<TSystemMenuModel> wrapper = new EntityWrapper<>();
//        wrapper.setSqlSelect("reqUrl");
//        wrapper.eq("type", GlobalConstant.MenuControlType.NO_LONGIN);
//        wrapper.ne("status", GlobalConstant.MODEL_DEL);
//        List<TSystemMenuModel> list = systemMenuService.selectList(wrapper);
//        if (CollectionUtils.isNotEmpty(list)) {
//            for (TSystemMenuModel tSystemMenuModel : list) {
//                if (StringUtils.isNotEmpty(tSystemMenuModel.getReqUrl())) {
//                    noLoginAndPer.put(tSystemMenuModel.getReqUrl(), "anon");
//                }
//            }
//        }
//        hashMap.putAll(noLoginAndPer);

        hashMap.put("/**", "user");
        shiroFilter.setFilterChainDefinitionMap(hashMap);
        return shiroFilter;
    }

    /**
     * 在方法中 注入 securityManager,进行代理控制
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {
        MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
        bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        bean.setArguments(new Object[] { securityManager });
        return bean;
    }

    /**
     * Shiro生命周期处理器: 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm) 在实现了Destroyable接口的Shiro bean销毁时调用
     * Destroyable接口回调(例如:DefaultSecurityManager)
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 启用shrio授权注解拦截方式，AOP式方法级权限检查
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}
